
#include "hi_type.h"
#include "mpi_sys.h"
#include "hi_ext_util.h"
#include "plug_base64.h"
#include "plug_opencv_image.h"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/imgcodecs/legacy/constants_c.h"


#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

/** Pointer Check */
#define CHECK_POINTER(p, errcode)    \
    do {                                        \
        if (!(p)) {                             \
            LOGE("pointer[%s] is NULL\n", #p); \
            return errcode;                     \
        }                                       \
    } while (0)


#define BASE64_JPEG_HEAD                "data:image/jpeg;base64,"

//jpg图像格式定义
typedef struct IMAGE_ENCODED_s {
    HI_S32 s32Width;                    //宽度(以像素为单位)
    HI_S32 s32Height;                   //高度(以像素为单位)
    HI_S32 s32Size;                     //图片buff所占字节数
    HI_U8 *pu8Data;                     //图像数据指针
}IMAGE_ENCODED_S;


static HI_S32 PLUG_OPENCV_IMAGE_ImageToCVMat(IVE_IMAGE_S* pstImage, cv::Mat &imageMat)
{
    CHECK_POINTER(pstImage, HI_FAILURE);
    switch(pstImage->enType)
    {
        case IVE_IMAGE_TYPE_YUV420SP:
        {
            HI_S32 s32FrameLen = pstImage->u32Height * 3 / 2 * pstImage->au32Stride[0];
            HI_CHAR *pazImg = (char *)HI_MPI_SYS_MmapCache(pstImage->au64PhyAddr[0], s32FrameLen);
            if (NULL == pazImg)
            {
                LOGE("HI_MPI_SYS_MmapCache failed\n");
                break;
            }

            cv::Mat YUVMat = cv::Mat(pstImage->u32Height * 3 / 2, pstImage->au32Stride[0], CV_8UC1, (uint8_t*)(uintptr_t)pazImg);
            cv::Mat OriMat;
            cv::cvtColor(YUVMat, OriMat, CV_YUV2BGR_NV21);
            cv::resize(OriMat, imageMat, cv::Size(pstImage->au32Stride[0] / PLUG_OPENCV_IMAGE_RESIZE, pstImage->u32Height / PLUG_OPENCV_IMAGE_RESIZE));
            HI_MPI_SYS_Munmap(pazImg, s32FrameLen);
            break;
        }
        //TODO:more format can be supported
        default:
        {
            LOGE("unsupported format : %d\n", pstImage->enType);
            return HI_FAILURE;
        }
    }
    return HI_SUCCESS;
}


static HI_S32 PLUG_OPENCV_IMAGE_EncodeImage(IVE_IMAGE_S* pstImage, IMAGE_ENCODED_S *pstImageEncoded, const char* suffix)
{
    CHECK_POINTER(pstImage, HI_FAILURE);
    CHECK_POINTER(pstImageEncoded, HI_FAILURE);
    CHECK_POINTER(suffix, HI_FAILURE);
    cv::Mat imageBgrMat;
    HI_S32 s32Ret = PLUG_OPENCV_IMAGE_ImageToCVMat(pstImage, imageBgrMat);
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_OPENCV_IMAGE_imageToCVMat failed\n");
        return HI_FAILURE;
    }

    std::vector<unsigned char> imageEncodedVector;
    if(!cv::imencode(suffix, imageBgrMat, imageEncodedVector))
    {
        LOGE("cv::imencode failed, suffix %s\n", suffix);
        return HI_FAILURE;
    }
    pstImageEncoded->s32Height = imageBgrMat.rows;
    pstImageEncoded->s32Width = imageBgrMat.cols;
    pstImageEncoded->s32Size = imageEncodedVector.size();
    pstImageEncoded->pu8Data = (unsigned char*)malloc(sizeof(unsigned char) * pstImageEncoded->s32Size);
    if (NULL == pstImageEncoded->pu8Data)
    {
        LOGE("malloc failed, size %d\n", pstImageEncoded->s32Size);
        return HI_FAILURE;
    }
    memcpy(pstImageEncoded->pu8Data, &imageEncodedVector[0], pstImageEncoded->s32Size);
    return HI_SUCCESS;
}


/*jpeg数据转base64*/
static HI_S32 PLUG_OPENCV_IMAGE_Jpeg2Base64(HI_CHAR *pazDataIn, HI_S32 s32InLen, HI_CHAR* pazBase64Out)
{
    if (NULL == pazDataIn || NULL == pazBase64Out || s32InLen <= 0) {
        return HI_FAILURE;
    }
    
    HI_S32 s32Base64Len = 0;
    if (s32InLen % 3 == 0) {
        s32Base64Len = s32InLen / 3 * 4;
    }
    else {
        s32Base64Len = (s32InLen / 3 + 1) * 4;
    }

    sprintf(pazBase64Out, BASE64_JPEG_HEAD);
    if (0 != base64_encode_bin((unsigned char*)pazDataIn, s32InLen, &pazBase64Out[strlen(BASE64_JPEG_HEAD)], s32Base64Len))
    {
        LOGE("error: get_face_by_frame_util base64_encode_bin failed\n");
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

HI_S32 PLUG_OPENCV_IMAGE_YUV420SP2JpegBase64(IVE_IMAGE_S* pstImage, HI_CHAR **ppazBase64Out, HI_S32 *ps32Base64Len)
{
    CHECK_POINTER(pstImage, HI_FAILURE);
    CHECK_POINTER(ppazBase64Out, HI_FAILURE);
    CHECK_POINTER(ps32Base64Len, HI_FAILURE);

    *ppazBase64Out = HI_NULL;
    *ps32Base64Len = 0;

    HI_S32 s32Ret = HI_SUCCESS;
    IMAGE_ENCODED_S stImgEncoded = { 0 };
    s32Ret = PLUG_OPENCV_IMAGE_EncodeImage(pstImage, &stImgEncoded, ".jpg");
    if (HI_SUCCESS != s32Ret)
    {
        LOGE("PLUG_OPENCV_IMAGE_EncodeImage failed\n");
    }

    HI_CHAR *pazJpegBase64 = NULL;
    HI_S32 s32JpegBase64Len = 0;
    s32JpegBase64Len = base64_encode_len(stImgEncoded.s32Size) + strlen(BASE64_JPEG_HEAD);
    pazJpegBase64 = (char*)malloc(s32JpegBase64Len * sizeof(HI_CHAR));
    if (NULL == pazJpegBase64)
    {
        free(stImgEncoded.pu8Data);
        LOGE("pazJpegBase64 malloc failed, size %d\n", s32JpegBase64Len);
        return HI_FAILURE;
    }
    memset(pazJpegBase64, 0, s32JpegBase64Len);
    s32Ret = PLUG_OPENCV_IMAGE_Jpeg2Base64((HI_CHAR*)stImgEncoded.pu8Data, stImgEncoded.s32Size, pazJpegBase64);
    if (0 != s32Ret)
    {
        LOGE("capturedFaceJPGBase64 jpeg_data_to_base64 failed, ret %d\n", s32Ret);
        free(stImgEncoded.pu8Data);
        free(pazJpegBase64);
        return HI_FAILURE;
    }

    free(stImgEncoded.pu8Data);
    *ppazBase64Out = pazJpegBase64;
    *ps32Base64Len = s32JpegBase64Len;
    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */

